package jee.boot.module.mail.sender;

import jee.boot.common.basic.ServiceException;
import jee.boot.common.utils.StringUtils;
import jee.boot.module.mail.config.EmailAuthenticator;
import jee.boot.entity.mail.Mail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import javax.activation.DataHandler;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.TimeZone;
import java.util.UUID;

/**
 * 邮件发送接口
 */
@Component
public class DefaultMailSender {
    /**
     * 日志对象
     */
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${spring.mail.username}")
    private String MAIL_SENDER;
    @Value("${spring.mail.personal:}")
    private String MAIL_PERSONAL;
    @Value("${spring.mail.password}")
    private String MAIL_PWD;
    @Value("${spring.mail.host}")
    private String MAIL_HOST;
    @Autowired
    private JavaMailSender javaMailSender;


    /**
     * 发送邮件
     *
     * @param entity
     */
    public void send(Mail entity) {
        try {
            MimeMessage mimeMailMessage = javaMailSender.createMimeMessage();
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true,"UTF-8");
            entity.setFromMail(MAIL_SENDER);
            InternetAddress from = new InternetAddress(MAIL_SENDER, MAIL_PERSONAL);
            mimeMessageHelper.setFrom(from);
            if (StringUtils.isNotBlank(entity.getToMail())) {
                mimeMessageHelper.setTo(StringUtils.split(entity.getToMail(), ";"));
            }
            if (StringUtils.isNotBlank(entity.getCcMail())) {
                mimeMessageHelper.setCc(StringUtils.split(entity.getCcMail(), ";"));
            }
            if (StringUtils.isNotBlank(entity.getBccMail())) {
                mimeMessageHelper.setBcc(StringUtils.split(entity.getBccMail(), ";"));
            }
            mimeMessageHelper.setSubject(entity.getSubject());
            mimeMessageHelper.setText(entity.getText(), true);
            //加载邮件附件
            if (!entity.getAttachmentList().isEmpty()) {
                for (File file : entity.getAttachmentList()) {
                    mimeMessageHelper.addAttachment(file.getName(), file);
                }
            }
            logger.info("邮件发送开始:" + entity.toString());
            // 邮件发送
            javaMailSender.send(mimeMailMessage);
            logger.info("邮件发送成功:" + entity.toString());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1424);
        }
    }

    /**
     * 发送日历类邮件
     *
     * @param entity
     */
    public void sendCalendarMail(Mail entity) {
        // 收件人信息
        String toMail = entity.getToMail();
        // 抄送人信息
        String ccMail = entity.getCcMail();
        // 暗送人信息
        String bcMail = entity.getBccMail();
        // 邮件标题
        String subject = entity.getSubject();
        // 邮件描述
        String text = entity.getText();
        // 地点信息
        String location = entity.getLocation();

        // 获取会议开始结束时间
        String startTime = this.getISO8601Timestamp(entity.getStartDateTime());
        String endTime = this.getISO8601Timestamp(entity.getEndDateTime());

        try {
            // 设置参数
            Properties props = getMailProperties();

            // 设置发件人信息
            Session session;
            Authenticator authenticator = new EmailAuthenticator(MAIL_SENDER, MAIL_PWD);
            session = Session.getInstance(props, authenticator);
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(MAIL_SENDER));

            // 设置收件人
            if (!(toMail == null || toMail.length() <= 0)) {
                for (String mailUser : toMail.split(";")) {
                    message.addRecipient(Message.RecipientType.TO, new InternetAddress(mailUser));
                }
            }

            // 设置抄送人
            if (!(ccMail == null || ccMail.length() <= 0)) {
                for (String mailUser : ccMail.split(";")) {
                    message.addRecipient(Message.RecipientType.CC, new InternetAddress(mailUser));
                }
            }

            // 设置暗送人
            if (!(bcMail == null || bcMail.length() <= 0)) {
                for (String mailUser : bcMail.split(";")) {
                    message.addRecipient(Message.RecipientType.BCC, new InternetAddress(mailUser));
                }
            }

            // 设置邮件内容
            message.setSubject(subject);

            // 获取邮件代码
            StringBuffer buffer = this.getMailCode(startTime, endTime, subject, text, location);

            // 转换为日历邮件的方式发送
            BodyPart messageBodyPart = new MimeBodyPart();
            messageBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource(buffer.toString(), "text/calendar;method=REQUEST;charset=\"UTF-8\"")));
            Multipart multipart = new MimeMultipart();
            multipart.addBodyPart(messageBodyPart);

            message.setContent(multipart);
            Transport.send(message);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1424);
        }
    }

    /**
     * 获取邮件CODE
     *
     * @param startTime
     * @param endTime
     * @param subject
     * @param text
     */
    private StringBuffer getMailCode(String startTime, String endTime, String subject, String text, String location) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("BEGIN:VCALENDAR\n"
                + "PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN\n"
                + "VERSION:2.0\n"
                + "METHOD:REQUEST\n"
                + "BEGIN:VEVENT\n"
                + "ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:" + MAIL_SENDER + "\n"
                + "ORGANIZER:MAILTO:" + MAIL_SENDER + "\n"
                + "DTSTART:" + startTime + "\n"
                + "DTEND:" + endTime + "\n"
                + "LOCATION:" + location + "\n"
                //如果id相同的话，outlook会认为是同一个会议请求，所以使用uuid。
                + "UID:" + UUID.randomUUID().toString() + "\n"
                + "CATEGORIES:\n"
                + "DESCRIPTION:" + text + "\n\n"
                + "SUMMARY:" + subject + "\n" + "PRIORITY:5\n"
                + "CLASS:PUBLIC\n" + "BEGIN:VALARM\n"
                + "TRIGGER:-PT15M\n" + "ACTION:DISPLAY\n"
                + "DESCRIPTION:Reminder\n" + "END:VALARM\n"
                + "END:VEVENT\n" + "END:VCALENDAR");

        return buffer;
    }

    /**
     * 设置邮件参数
     */
    private Properties getMailProperties() {
        // 设置属性
        Properties props = new Properties();

        // 设置邮件的基本属性
        props.put("mail.smtp.host", MAIL_HOST);
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.ssl", "true");
        props.put("mail.debug", "true");
        props.put("mail.smtp.auth", "true"); //这样才能通过验证

        // 防止25端口被封掉，采用ssl方式发送信息
        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        props.put("mail.smtp.port", "465");
        props.put("mail.smtp.socketFactory.port", "465");

        // 返回设置好的属性
        return props;
    }

    /**
     * 获取标准时间格式
     * 否则日历日期显示异常
     *
     * @param date
     * @return
     */
    private static String getISO8601Timestamp(Date date) {
        TimeZone tz = TimeZone.getTimeZone("UTC");
        DateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
        df.setTimeZone(tz);
        String nowAsISO = df.format(date);
        return nowAsISO;
    }
}
